package com.ruiysoft.service.integral.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruiysoft.controller.integral.vo.IntegralRechargeVo;
import com.ruiysoft.entity.integral.Integral;
import com.ruiysoft.entity.integral.IntegralCurrent;
import com.ruiysoft.entity.sys.SysShopPersonnel;
import com.ruiysoft.mapper.integral.IntegralMapper;
import com.ruiysoft.pub.lock.Lock;
import com.ruiysoft.service.integral.IIntegralCurrentService;
import com.ruiysoft.service.integral.IIntegralService;
import com.ruiysoft.service.integral.bean.IntegralCondition;
import com.ruiysoft.service.integral.consts.TradeType;
import com.ruiysoft.service.mdse.consts.MdseEnum;
import com.ruiysoft.service.sys.redis.IRedisSysUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author liyahui
 * @since 2019-04-13
 */
@Service
public class IntegralServiceImpl extends ServiceImpl<IntegralMapper, Integral> implements IIntegralService {

    @Resource
    IRedisSysUserService iRedisSysUserService;
    @Resource
    private IIntegralCurrentService integralCurrentService;
    @Override
    public QueryWrapper<Integral> getWapperByCondition(IntegralCondition condition) {
        return new QueryWrapper<Integral>().eq(StringUtils.isNotBlank(condition.getPhone()),"phone",condition.getPhone())
                .eq(condition.getShopId()!=null,"shop_id",condition.getShopId())
                .eq(condition.getIsUse()!=null,"is_use",condition.getIsUse())
                .orderBy(false,StringUtils.isNotBlank(condition.getOrderByClause()),condition.getOrderByClause());
    }

    @Override
    //隔离级别为READ_COMMITTED 可读取其他事务提交的更新。默认级别会读取老版本数据导致判断错误
    @Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,isolation = Isolation.READ_COMMITTED)
    public void recharge(IntegralRechargeVo recharge, Long shopId) {
        alterHandle(shopId,recharge.getPhone(),recharge.getIntegral(),TradeType.RECHARGE, IdWorker.getId()+"");
    }

    @Override
    //隔离级别为READ_COMMITTED 可读取其他事务提交的更新。默认级别会读取老版本数据导致判断错误
    @Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,isolation = Isolation.READ_COMMITTED)
    @Lock(key = "integralTrade",id="#tradeNo",expire = 2*60*1000,acquireTimeout = 99L)
    public void consume(Long shopId, Long userId, Long integral, String tradeNo) {
        IntegralCondition condition = new IntegralCondition();
        condition.setTradeType(TradeType.CONSUME);
        condition.setTradeNo(tradeNo);
        condition.setOrderByClause("createtime");
        int cout = integralCurrentService.count(integralCurrentService.getWapperByCondition(condition));
        if(cout>0){
            throw new RuntimeException("重复提交");
        }
        String phone = Optional.ofNullable(iRedisSysUserService.selectSysShopPersonnelById(userId)).map(SysShopPersonnel::getMobile).orElseThrow(()->new RuntimeException("未绑定积分账户"));
        alterHandle(shopId,phone,integral,TradeType.CONSUME,tradeNo);
    }

    @Override
    //隔离级别为READ_COMMITTED 可读取其他事务提交的更新。默认级别会读取老版本数据导致判断错误
    @Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,isolation = Isolation.READ_COMMITTED)
    @Lock(key = "integralTrade",id="#tradeNo",expire = 2*60*1000,acquireTimeout = 99L)
    public void refund(Long shopId, Long userId, Long integral, String tradeNo) {
        IntegralCondition condition = new IntegralCondition();
        condition.setTradeType(TradeType.REFUND);
        condition.setTradeNo(tradeNo);
        int cout = integralCurrentService.count(integralCurrentService.getWapperByCondition(condition));
        if(cout>0){
            throw new RuntimeException("重复提交");
        }
        String phone = Optional.ofNullable(iRedisSysUserService.selectSysShopPersonnelById(userId)).map(SysShopPersonnel::getMobile).orElseThrow(()->new RuntimeException("未绑定积分账户"));
        alterHandle(shopId,phone,integral,TradeType.REFUND,tradeNo);
    }

    //隔离级别为READ_COMMITTED 可读取其他事务提交的更新。默认级别会读取老版本数据导致判断错误
    @Lock(key = "integralHandle",id="#phone",expire = 2*60*1000,acquireTimeout = 99L)
    @Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,isolation = Isolation.READ_COMMITTED)
    void alterHandle(Long shopId, String phone, Long integral, TradeType tradeType, String tradeNo) {
        phone = Optional.ofNullable(phone).filter(StringUtils::isNotBlank).orElseThrow(()->new RuntimeException("手机号不能为空"));
        shopId = Objects.requireNonNull(shopId,"shopId不能为空");
        integral = Objects.requireNonNull(integral,phone+": 积分不能为空");
        tradeType = Objects.requireNonNull(tradeType,"处理方式不能为空");

        Integral dto = getOne(getWapperByCondition(new IntegralCondition().setShopId(shopId).setPhone(phone)));
        //增加积分
        Long finalIntegral = integral;
        if(tradeType.isAdd()){
            dto = Optional.ofNullable(dto).map(a->a.setIntegral(a.getIntegral()+ finalIntegral).setUpdateTime(new Date()))
                    .orElse(new Integral().setIntegral(integral)
                            .setIsUse(MdseEnum.IsUseEnum.YES.getIndex())
                            .setPhone(phone).setShopId(shopId)
                            .setUpdateTime(new Date()));
            //如果没有积分记录新增（增加积分不处理禁用情况）
            saveOrUpdate(dto);
        }else{
//            Optional.ofNullable(Objects.requireNonNull(dto,"账户不存在"))
//                    .filter(a->Objects.equals(a.getIsUse(),MdseEnum.IsUseEnum.NO.getIndex())).orElseThrow(()->new RuntimeException("账户被禁用"));
            if(dto.getIntegral()<finalIntegral){
                throw new RuntimeException("账户余额不足");
            }
            updateById(dto.setIntegral(dto.getIntegral()- finalIntegral).setUpdateTime(new Date()));
        }
        //增加流水记录
        integralCurrentService.save(new IntegralCurrent()
                .setCreatetime(new Date())
                .setIntegral(integral)
                .setIntegralId(dto.getId())
                .setShopId(shopId)
                .setTradeType(tradeType.getIndex())
                .setPhone(phone)
                .setTradeNo(tradeNo)
        );

    }
}
